\section{Manejo de interrupciones}
\label{sec:interrupciones}

A menudo un sistema operativo es interrumpido por alg'un otro dispositivo, cortando el ciclo de ejecuci'on 
actual, pasando a ejecutar c'odigo espec'ifico para tratar esta situaci'on. Debemos distinguir tres tipos de interrupciones que soporta el procesador: excepciones, interrupciones de hardware e interrupciones de software. Las primeras son generadas por el procesador debido a que no pudo completar una instrucci'on, las segundas son interrupciones generadas desde dispositivos de entrada y/o salida y las 'ultimas pueden ser llamadas desde el c'odigo del kernel o tareas.

La arquitectura provee un mecanismo para asignar rutinas de atenci'on para dichas interrupciones. El mismo consiste en definir una tabla (IDT) que al igual que en la GDT y la LDT continene un descriptor en cada entrada. La tabla tiene 256 posiciones, determinando la cantidad m'axima de interrupciones que puede manejar el sistema. Cada descriptor, entre otras cosas, especifica la direccion de memoria de la rutina de atencion de dicha interrupci'on.

Para completar las entradas en la IDT, se utiliz'o una macro provista por la c'atedra (IDT$\_$ENTRY), sin embargo
hubo que modificarla previamente para que el offset sea el segmento de c'odigo, de lo contrario no funcionaria.

Existen tres tipos de descriptores de puerta: \textit{Task Gate}, \textit{Interrupt Gate} y \textit{Trap Gate}.
Es importante entender las diferencias a la hora de programar ya que el funcionamiento 
del c'odigo puede verse afectado radicalmente. El \textit{Task Gate} permite crear una tarea con contexto limpio para atender la interrupci'on, mientras que los otros dos descriptores atienden la interrupci'on utilizando el entorno de la tarea interrumpida. La principal diferencia entre el \textit{Trap Gate} y el \textit{Interrupt Gate} es que cuando se termina la interrupci'on, el primero retorna del handler y vuelve a ejecutar la misma instrucci'on que la gener'o, mientras que la segunda retorna a la siguiente instrucci'on. 

En general para este trabajo pr'actico, si no especifcamos lo contrario, estaremos trabajando con Interrupt Gates.

\subsection{Excepciones}
\subsubsection{Definiendo rutinas de atenci'on}
En este apartado nos ocuparemos del primer tipo de interrupciones, ya que son necesarias tempranamente para ayudarnos a detectar los errores en nuestro c'odigo. Las rutinas de atenci'on fueron definidas en  en el archivo isr.asm, se crearon 20 rutinas, una por cada
excecpci'on del procesador en modo protegido. 

Para definir una rutina se utiliz'o una macro llamada \textbf{DEFINIR$\_$HANDLER} que toma como par'ametro el 
n'umero de excepci'on. Dicha macro escribe un mensaje con la descripci'on de la misma, su mnem'onico y los datos pedidos en el enunciado. A continuaci'on exhibimos la definici'on de la macro:\\
\begin{verbatim}
 %macro DEFINIR_HANDLER 1
global _isr%1
_isr%1:
    IMPRIMIR_TEXTO msg%1, msg%1_len, 0x07, 1, 1
    call dump_pantalla
    jmp $
%endmacro
\end{verbatim}

Notemos que luego de realizar \texttt{dump$\_$pantalla} la rutina cuelga asi podemos observar el mensaje impreso por pantalla. 

Una vez definidos los handlers y completas las entradas en la IDT procedemos de manera an'aloga a la GDT, utilizando la instrucci'on \texttt{lidt} para cargar la IDT.

\subsubsection{Imprimiendo datos en pantalla}

Cuando se produce una excepci'on, seria prudente saber que tipo y que valores guardan los registros del cpu
en ese momento, adem'as de tener un conocimiento de los valores de la pila y direcciones de retorno apiladas (considerando
un l'imite de llamados).

Para implementar esto se realiz'o la funci'on \texttt{dump$\_$pantalla} que se encarga de hacer 
todo lo necesario para mostrar en pantalla lo pedido (salvando los registros) y se compone de tres partes principales: imprimir los registros, el backtrace y stack.

\paragraph{Imprimiendo registros}

Para imprimir los registros armamos una macro llamada \texttt{IMPRIMIR$\_$LINEA} que imprime un registro
en una posici'on dada de la pantalla, toma tres parametros: el registro y las coordenadas de la pantalla. Hicimos que los textos que contenian los nombres de los registros sean siempre de la misma cantidad de caracteres para los registros de prop'osito general y para los de segmento.

Para mostrar el valor de los registros se utiliza la macro \texttt{DWORD$\_$TO$\_$HEX} provista por la c'atedra y modificada por nosotros para convertir el valor de un registro en ASCII a'un si es menor a 32 bits. De esta manera lo pudimos utlizar tambien para los registros de segmento.
 
Adem'as se intent'o que la pantalla sea los m'as similar posible a la del enunciado, el 'unico medio que tuvimos para saber en que orden imprimir y en que posiciones de la pantalla fue un dibujo y no una especificaci'on menos ambigua por lo tanto tuvimos que mantener el criterio de aceptaci'on que sea los m'as similar posible y que los datos sean identificables y legibles para un potencial an'alisis.


\paragraph{Imprimiendo la pila}

Para imprimir el contenido de el \textit{stack} tenemos en cuenta que el registro ESP apunta al tope de la pila del kernel. Pero no queremos considerar la direcci'on de retorno al llamado de \texttt{dump$\_$pantalla} por lo tanto imprimimos el contenido a partir de ESP+4 hasta ESP+24, que son los ultimos 6 valores almacenados en la pila. 

Una forma de verificar si esto se hizo correctamente es ver si coniciden los valores de los registros CS, EIP y EFLAGS, con los apilados automaticamente por el procesador cada vez que se produce una interrupci'on. Hay que tener en cuenta que si la excepci'on tiene ERROR CODE, este se pasa por la pila, pero el ESP no apunta a 'este. Esto es sustancial para  el funcionamiento apropiado del proceso de lo contrario no sabr'iamos si lo 'ultimo apilado es un c'odigo de error o un EIP. Recordemos que no todas las excepciones devuelven un c'odigo de error. 

Para imprimir los valores de el \textit{stack} usamos la macro \texttt{PRINT\_HEXA} de gran utilidad para manipular
valores en ASCII y al igual que \texttt{PRINT\_LINEA} no necesita guardar los registros porque la implentacio'n de la c'atedra 
los hace por nosotros.

\paragraph{Imprimiendo el backtrace}

Para poder construir un backtrace es necesario poder identificar todas las direcciones de retorno almacenadas en la pila. Un manera de hacerlo es que las funciones que utilicemos respeten convenci'on C. La misma establece que el registro EBP contiene la base de la pila y que dicho valor es apilado al inicio de la funci'on. Por lo tanto si vamos reemplazando el valor de EBP por el valor del contenido de EBP, las sucesivas direcciones de retorno se encuentran siempre en EBP+4.

Hay que tener en cuenta ciertas precauciones por ejemplo considerar que no hay cambio de privilegio y el handler ser'a ejecutado con el 
mismo nivel que un proceso a interrumpir, pues sino habria un cambio de pila.

El cuadro \ref{tab:stack} grafica las posiciones de las direcciones de retorno dentro de la pila.

\begin{table}[H]
\begin{center}
\begin{tabular}{|c|}
\hline
\sc{ESTADO PILA}  \\ \hline
Error Code \\ \hline
EIP  \\ \hline
CS \\ \hline
EFLAGS \\ \hline
RET \\ \hline
... \\ \hline
... \\ \hline
EBP VIEJO \\ \hline
RET \\ \hline
... \\ \hline
... \\ \hline
EBP VIEJO \\ \hline
RET \\ \hline
\end{tabular}
\caption{contenido de la pila al producirse una interrupci'on}
\label{tab:stack}
\end{center}
\end{table} 

Es importante se'nalar que debemos saber hasta que punto imprimir las direcciones de retorno, especialmente manejar el caso en que hay menos de 5 llamados. Es decir, debemos definir cual ser'a la condici'on de corte. Para esto cada vez que modificamos el EBP revisamos si llegamos a la direcci'on base de la pila de kernel, y por lo tanto no hay mas pila ni direcciones de retorno. Cabe destacar que inicialmente comparamos con la direccion de memoria 0x17000, pero cuando implementamos las tareas tuvimos que comparar tambien con la direccion de memoria 0x30000 que la direccion de memoria virtual donde esta definida la pila del proceso. Recordemos que esto se debe a que el handler de interrupci'on se ejecutara utilizando el contexto de la tarea interrumpida.

Una buena forma de probar la implementaci'on de lo hecho hasta aqu'i es  generando una excepci'on y viendo
como procede el handler. Hay que tener en cuenta que ciertas excepciones no pueden generarse a'un, por 
ejemplo \textit{page fault}, debido a que no tenemos definido un esquema de paginaci'on todav'ia.

Si embargo si podemos generar un \textit{divide error}. Una forma es cargar un en un registro de prop'osito genera distinto de eax el n'umero cero, cargar en eax un cero, y realizar la instrucci'on \texttt{div \%reg}. Otra forma es llamar a int 0.

Otro tipo de excepcion que puede generarse es un error de proteccion general tratando de acceder fuera del l'imite
por ejemplo ejecutando mov eax, [es:0xFFFFFFFF] (ver ap'endice \ref{sec:debug}).

\subsubsection{Preguntas}

\paragraph*{Pregunta 2:} ?`C'omo se puede hacer para generar una excepci'on sin utilizar la 
instrucci'on int? Mencione al menos 3 formas posibles. \\
\textbf{Respuesta:} Una manera es generando una division por cero, otra es escribiendo con el segmento de video en un offset que exceda el limite, tambi'en con un opcode inv'alido. Adem'as es posible entrara a cualquier handler con la instruccion \texttt{int} siempre y cuando los permisos seteados correspondan.

\paragraph*{Pregunta 3:} ?`Cu'ales son los valores del stack? 
?` Qu'e significan? \\
\textbf{Respuesta:} Son los registros EFLAGS, CS, EIP y las direcciones de retorno que se fueron apilando o variables de algun tipo. 

\paragraph*{Pregunta 4:} ?`Puede ser construido el backtrace si no se respeta la convenci'on C? 
?`Por qu'e ? \\
\textbf{Respuesta:} Si, pero es m'as dificil, tendr'iamos que generar una forma de encontrar las direcciones de retorno anteriores, tal
vez definiendo otra convenci'on y teniendo un seguimiento estricto de la base de la pila, que se va apilando y desapilando
a medida que se ejecutan procesos.



\subsection{Interrupciones de hardware}
\subsubsection{PIC}
El dispositivo encargado de lleguen las interrupciones de los dispositivos de entrada-salida es el PIC. El mismo es quien las administra y las ordena por prioridad antes de pasarselas al procesador. Sin embargo para utilizar este mecanismo hay que solucionar un conflicto historico: las IRQ 
ya est'an mapeadas a las interrupciones 0x8 a 0xF, pero intel reserv'o las interrupciones de 0  a 31, de all'i el
problema.

Para reasignarlas utilizamos las tres funciones que la c'atedra provee. La secuencia de instrucciones a realizar es la siguiente: deshabilitar pic, resetear pic y habilitar pic. Una vez hecho esto podemos empezar a trabajar ahora con las interrupciones de hardware. Especificamente lo haremos rutinas de atenci'on de teclado y reloj.

Es interesante notar que si se habilitan las interrupciones de hardware (instrucci'on \texttt{sti}) antes de reasignar el PIC se producir'ia una interrupci'on de reloj, pero la misma entraria al handler ubicado en la entrada n'umeo 8 de la IDT, donde tenemos definida la excepci'on de doble falta. Realizamos una comprobaci'on emp'irica de esta conjetura que resulto exitosa.

\subsubsection{Reloj y Teclado}
Un vez reasignado el PIC, la interrupci'on de reloj (la m'as prioritaria) queda mapeada a la entrada n'umero 32 de la IDT, mientras que la del teclado (la segunda mas prioritaria) que asignada en la posici'on 33. Por lo tanto agregamos esas entradas en la IDT de la misma manera que para las excepciones (utilizando Interrupt Gates). Sin embargo las rutinas de atenci'on fueron diferentes en este caso.

La rutina que atiende el reloj llama a la funci'on provista por la c'atedra \texttt{proximo$\_$reloj} que imprime un reloj en la esquina inferior derecha de la pantalla cada vez que se llama, dando la sensaci'on de que algo se mueve.

El handler de teclado por su parte lee del puerto de entrada-salida n'umero 60 (\texttt{in al, 60}, buffer teclado) y calcula la tecla obtenida(\textit{scan code}). Para esto lo primero que hace es descartar cuando la tecla es soltada\footnote{Cabe recordar que por cada pulsaci'on de tecla tenemos dos interrupciones: una por presionarla y otra por soltarla(\textit{break code}).}, revisando el s'eptimo bit del c'odigo (si esta encendido es un \textit{break code}). Luego, como s'olo nos interesan los n'umeros (y los mismos estan consecutivos en el teclado desde la posici'on 0x2 hasta la 0xC) descarta preguntando por mayor y menor. Lo siguiente es distinguir el cero, eso se hace facilmente preguntando si el c'odigo es igual a 0xB, en tal caso ya sabemos que el ASCII es el 0x30. En este punto ya filtramos todos los casos bordes, por lo que resta calcular el caso generico para los n'umeros del uno al nueve. Teniendo en cuenta que el c'odigo del uno es 0x2 y el ascii es 0x31, es f'acil deducir que si sumamos 0x29 a cada c'odigo obtenemos el ascii deseado. Una vez obtenido lo imprimimos en pantalla como pide el enunciado.

Adem'as ambos handlers no s'olo resguardan el estado de todos los registros como cualquier rutina de atenci'on interrupci'on, sino que tambi'en llaman a la funci'on fin$\_$intr$\_$pic1 para notificar al PIC que se atendi'o la interrupci'on.

\subsection{Interrupciones de software}

Por 'ultimo queremos atender las interrupciones de software 66, 77, 88 y 99 como pide el enunciado. Para eso, creamos nuevas entradas en la IDT (de la misma manera que para las otras interrupciones) y definimos los handlers correspondientes en isr.asm. Para cumplir lo pedido (dejar el valor especificado en el enunciado en EAX) y salvar todos los registros, apilamos todos los registros, los EFLAGS, notificamos al PIC la atenci'on de la interrupcion, desapilamos los EFLAGS, los registros, y justo antes de salir movemos a EAX el valor arbitrario definido para cada rutina.






